#region license
# This file is part of Grawlix.
# 
# Grawlix is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
# 
# Grawlix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public
# License along with Grawlix.  If not, see
# <http://www.gnu.org/licenses/>.
#endregion

"""An AST Attribute that goes through multiple stages."""

namespace Grawlix.Meta
import System
import Boo.Lang.Compiler

transient class StagedAstAttribute(AbstractAstAttribute):
"""
An AST Attribute that is applied over multiple stages.
Remarks: This attribute is based upon many unchecked conventions.
"""
	
	final override def Apply([required] targetNode as Ast.Node):
	"""
	Applies the attribute to the specified node.
	Param targetNode: The node to apply to.
	"""
		targetNode.Annotate(_key, self)
		if Context[_key] is null:
			Context[_key] = _key
			pipeline = InnerPipeline(
				Context,
				Steps.MacroAndAttributeExpansion)
			pipeline.Add(ProcessDefinitionStep())
			pipeline = InnerPipeline(
				Context,
				Steps.CheckGenericConstraints)
			pipeline.Add(ProcessMemberStep())
			pipeline = InnerPipeline(
				Context,
				Steps.PreProcessExtensionMethods)
			pipeline.Add(ProcessStatementStep())
		ProcessSyntax(targetNode)
	
	virtual def ProcessSyntax([required] target as Ast.Node):
	"""
	Does simple AST stage processing of the node.
	Param target: The node the attribute is applied to.
	Remarks: Valid operations at this point only
		involve inspecting and editing the target node or
		its child nodes.
		
		The <NameResolutionService> is not valid to call, and
		entities should not be inspected or generated.
		
		This stage should only be used for simple lexical
		transformations or recursive interaction with
		other AST Attributes/Macros.
		
		In later steps, the node itself may not be removed or replaced.
		The only type declarations or members that may be
		removed or replaced after this step are
		those that were created by this attribute after this step.
	"""
		pass
	
	virtual def ProcessDefinition():
	"""
	Does any processing which may generate new types.
	Remarks: Valid operations at this point are AST inspection and
		modification of the target nodes or its child nodes, or
		adding members to the parents of the target node.
		
		The <NameResolutionService> is still invalid and
		entities should not be inspected or generated.
		
		Any new modules/types must be created by the end of this step.
		Base classes/types must be defined as well by this point.
		
		Since the <NameResolutionService> is not yet ready,
		type generation/inheritance cannot depend upon name resolution.
		However, classes and base types may be
		removed (but not added) in later steps.
	"""
		pass
	
	virtual def ProcessMember():
	"""
	Does member-level processing of the node.
	Remarks:
		At this stage, entities for types and type members are created.
		Base types are bound, but no members are overriden yet.
		The <NameResolutionService> works for all members.
		
		New classes cannot be added and inheritance cannot be affected.
		Classes and base types can, however, be removed.
		
		Type members can be added and removed at will, but
		signatures must be generated by the CodeGenerator.
		Return types and field types may not yet be defined.
		
		By the end of this stage, all declarations (both class and member),
		must be in their final state. Return types and field types
		may be left unknown.
	"""
		pass
	
	virtual def ProcessStatement():
	"""
	Does the final tweaking for this node.
	Remarks:
		At this stage, entities for types and type members are created.
		Base types are bound, but no members are overriden yet.
		The <NameResolutionService> works for all members.
		
		New classes cannot be added and inheritance cannot be affected.
		Classes and base types can, however, be removed.
		
		Type members can be added and removed at will, but
		signatures must be generated by the CodeGenerator.
		Return types and field types may not yet be defined.
		
		By the end of this stage, all declarations (both class and member),
		must be in their final state. Return types and field types
		may be left unknown.
	"""
		pass
	
	# HACK: May need to be changed over versions.
	public final override def Dispose():
	"""Keep this object valid."""
		GC.SuppressFinalize(self)

	internal static Key:
	"""The annotation key."""
		get:
			return _key
	
	private static _key = object()
	"""The annotation key."""

internal class AttributeProcessingStep(
	Steps.AbstractVisitorCompilerStep):
"""Run a process action on an attribute."""
	
	override def Run():
	"""Start the step."""
		Visit(CompileUnit)
	
	override def Visit([required] node as Ast.Node):
	"""
	Processes the node before visiting child nodes.
	Param node: The node to visit.
	"""
		if node.ContainsAnnotation(StagedAstAttribute.Key):
			Apply(node[StagedAstAttribute])
		return super(node)
	
	abstract def Apply(attribute as StagedAstAttribute):
	"""
	Applies the attribute.
	Param attribute: The attribute to apply.
	"""
		pass

internal class ProcessDefinitionStep(AttributeProcessingStep):
"""Apply the ProcessDefinition step to the attribute."""
	
	override def Apply([required] attribute as StagedAstAttribute):
	"""
	Applies the attribute.
	Param attribute: The attribute to apply.
	"""
		attribute.ProcessDefinition()

internal class ProcessMemberStep(AttributeProcessingStep):
"""Apply the ProcessMember step to the attribute."""
	
	override def Apply([required] attribute as StagedAstAttribute):
	"""
	Applies the attribute.
	Param attribute: The attribute to apply.
	"""
		attribute.ProcessMember()

internal class ProcessStatementStep(AttributeProcessingStep):
"""Apply the ProcessStatement step to the attribute."""
	
	override def Apply([required] attribute as StagedAstAttribute):
	"""
	Applies the attribute.
	Param attribute: The attribute to apply.
	"""
		attribute.ProcessStatement()
